package cn.hg.solon.youcan.web.admin.controller;

import java.util.List;
import java.util.function.Supplier;

import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.convert.Convert;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Param;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.MethodType;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.handle.Result;
import org.noear.solon.validation.annotation.Length;
import org.noear.solon.validation.annotation.NotEmpty;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hg.solon.youcan.common.annotation.Log;
import cn.hg.solon.youcan.common.constant.CacheConstants;
import cn.hg.solon.youcan.common.enums.BusinessType;
import cn.hg.solon.youcan.common.web.PearConfig;
import cn.hg.solon.youcan.common.web.PearMenuItem;
import cn.hg.solon.youcan.framework.cache.YoucanCacheService;
import cn.hg.solon.youcan.framework.satoken.SaUtil;
import cn.hg.solon.youcan.framework.service.LoginService;
import cn.hg.solon.youcan.framework.service.LogoutService;
import cn.hg.solon.youcan.framework.service.PearService;
import cn.hg.solon.youcan.system.entity.SysUser;
import cn.hg.solon.youcan.system.service.SysUserService;

/**
 * @author 胡高
 */
@Controller
@Mapping("/admin")
public class AdminIndexController extends BaseAdminController {

    private static String SERVICE_NAME = "主页面";

    private static String VIEW_BASE = "/admin/";

    @Inject
    private SysUserService userService;

    @Inject
    private LoginService loginService;

    @Inject
    private LogoutService logoutService;

    @Inject
    private YoucanCacheService cacheService;

    @Inject
    private PearService pearService;

    /**
     * 打开修改密码页面
     */
    @Mapping(path = "changePassword", method = MethodType.GET)
    @SaCheckLogin
    public ModelAndView changePassword(Context ctx) {
        this.log.info("{}打开修改密码页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "changePassword.html");

        return mav;
    }

    @Mapping(path = "config", method = MethodType.GET)
    @SaCheckLogin
    public PearConfig Config(Context ctx) {
        SysUser user = SaUtil.getCurrentUser();
        PearConfig config = this.cacheService.getOrStoreTag(
            CacheConstants.SYS_USER_KEY + ":index-config:" + user.getId(),
            CacheConstants.CACHE_SECONDS_ONE_HOUR, new Supplier<PearConfig>() {
                @Override
                public PearConfig get() {
                    return AdminIndexController.this.pearService.getConfig();
                }
            } , CacheConstants.SYS_USER_TAG);

        return config;
    }

    /**
     * 修改密码操作
     */
    @Mapping(path = "changePassword", method = MethodType.PUT)
    @Log(title = "重置密码", businessType = BusinessType.OTHER)
    @SaCheckLogin
    public Result<?> doChangePassword(Context ctx,
        @NotEmpty @Length(min = 6, max = 20, message = "旧密码长度必须 6 到 20 字符") String oldPassword,
        @NotEmpty @Length(min = 6, max = 20, message = "新密码长度必须 6 到 20 字符") String newPassword,
        @NotEmpty(message = "必须输入确认密码") String confirmPassword) {
        this.log.info("{}执行修改密码操作：", SERVICE_NAME);

        this.userService.resetPassword(StpUtil.getLoginIdAsInt(), oldPassword, newPassword, SaUtil.getCurrentUser());

        return Result.succeed();
    }

    /**
     * 登录操作
     */
    @Mapping(path = "login", method = MethodType.POST)
    public Result<?> doLogin(Context ctx,
        @NotEmpty String captchaVerification,
        @NotEmpty @Length(min = 3, max = 20, message = "账号长度必须 3 到 20 字符") String account,
        @NotEmpty @Length(min = 6, max = 20, message = "密码长度必须 6 到 20 字符") String password, String remember) {
        this.log.info("{}登录操作：", SERVICE_NAME);

        this.loginService.login(account, password, captchaVerification, CharSequenceUtil.equals("on", remember));

        return Result.succeed();
    }

    /**
     * 登出操作
     */
    @Mapping(path = "logout", method = {MethodType.GET, MethodType.DELETE})
    public Result<?> doLogout(Context ctx) {
        this.logoutService.logout();
        return Result.succeed();
    }

    /**
     * Home页面
     */
    @Mapping(path = "home", method = MethodType.GET)
    @SaCheckPermission(value = { "menu:system:home" })
    public ModelAndView home(Context ctx) {
        this.log.info("{}打开Home页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "home.html");

        return mav;
    }

    /**
     * 首页
     */
    @Mapping(path = "", method = MethodType.GET)
    @SaCheckLogin
    public ModelAndView index(Context ctx) {
        this.log.info("打开管理端页面：index.html");

        ModelAndView mav = new ModelAndView(VIEW_BASE + "index.html");

        mav.put("user", this.loginService.currentUser());

        return mav;
    }

    /**
     * 跳转入登录页面
     */
    @Mapping(path = "login", method = MethodType.GET)
    public ModelAndView login(Context ctx) {
        this.log.info("{}打开登录页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "login.html");

        if (StpUtil.isLogin()) {
            ctx.redirect((StrValidator.isBlank(Solon.cfg().serverContextPath()) ? "/" : Solon.cfg().serverContextPath())
                + "admin");
            return null;
        }

        // 将请求参数原样传递到页面
        mav.putAll(ctx.paramMap());

        return mav;
    }

    /**
     * 获取菜单数据
     */
    @Mapping(path = "menu", method = MethodType.GET)
    @SaCheckLogin
    public List<PearMenuItem> menu(Context ctx, @Param String ids) {
        this.log.debug("{}打开管理端菜单：ids={}", SERVICE_NAME, ids);
        SysUser user = SaUtil.getCurrentUser();

        List<PearMenuItem> menus = this.cacheService.getOrStoreTag(
            CacheConstants.SYS_USER_KEY + ":index-menu:" + user.getId() + ":" + ListUtil.of(ids),
            CacheConstants.CACHE_SECONDS_ONE_HOUR, new Supplier<List<PearMenuItem>>() {
                @Override
                public List<PearMenuItem> get() {
                    return AdminIndexController.this.pearService.buildMenu(user, Convert.toIntArray(SplitUtil.split(ids, ",")));
                }
            } , CacheConstants.SYS_USER_TAG);

        return menus;
    }
}
